
#ifndef _AGENT__TASK_SCHEDULER_H__INCLUDED_
#	error Should not include TaskSchedulerImpl.h directly, include TaskScheduler.h instead
#endif

#include "Utility/ErrorCode.h"
#include "Utility/ThreadPool.h"
#include <sys/time.h>

#ifndef SUBTRACT_TIMEVAL
#	define SUBTRACT_TIMEVAL(end, start)    ((end.tv_sec - start.tv_sec) * 1000000 + (end.tv_usec - start.tv_usec))
#endif

namespace nebula { namespace utility {

template <typename helper_type>
class TaskSchedulerImpl : public ThreadPool
{
public:
	typedef typename TaskScheduler<helper_type>::task_processor task_processor;

public:
	TaskSchedulerImpl(const char* name):
			ThreadPool(name),
			ptr_helper(NULL),
			task_proc(NULL),
			idle_sleep(1)
	{
	}
	~TaskSchedulerImpl(){}

public:
	virtual NEBULA_ERROR_CODE start(
			helper_type *helper,
			task_processor proc,
			unsigned int _idle_sleep,//ms
			unsigned int min_thread_count,
			unsigned int max_thread_count)
	{
		ptr_helper = helper;
		task_proc = proc;
		idle_sleep = _idle_sleep;
		return ThreadPool::start(min_thread_count, max_thread_count);
	}

protected:
	virtual void worker(ThreadContext* context)
	{
		if(! context)
			return;
		while (!context->quit.is_set())
		{
			struct timeval last_run;
			gettimeofday(&last_run, 0);

			bool repeat_immediately = false;
			context->status = THR_STATUS_BUSY;
			(ptr_helper->*(task_proc))(repeat_immediately);
			context->status = THR_STATUS_IDLE;

			if (!repeat_immediately)
			{
				struct timeval now;
				gettimeofday(&now, 0);
				const int to_wait = idle_sleep - SUBTRACT_TIMEVAL(now, last_run) / 1000;
				if (to_wait > 0)
					context->quit.wait(to_wait);
			}
		}
		context->status = THR_STATUS_TERMINATED;
	}

private:
	helper_type* ptr_helper;
	task_processor task_proc;
	unsigned int idle_sleep;

private:
    friend class TaskSchedulerTest;//For UT only
};

//-----------------------------------------------------------------------------------------
template <typename helper_type>
TaskScheduler<helper_type>::TaskScheduler(const char* name)
	: pImpl(new TaskSchedulerImpl<helper_type>(name))
{
}

template <typename helper_type>
NEBULA_ERROR_CODE TaskScheduler<helper_type>::start(
		helper_type *helper,
		task_processor proc,
		unsigned int idle_sleep,//ms
		unsigned int min_thread_count,
		unsigned int max_thread_count)
{
    return pImpl->start(helper, proc, idle_sleep, min_thread_count, max_thread_count);
}

template <typename helper_type>
NEBULA_ERROR_CODE TaskScheduler<helper_type>::stop()
{
    return pImpl->stop();
}

template <typename helper_type>
NEBULA_ERROR_CODE TaskScheduler<helper_type>::notify_to_stop()
{
	return pImpl->notify_to_stop();
}

template <typename helper_type>
NEBULA_ERROR_CODE TaskScheduler<helper_type>::set_thread_count_range(
			unsigned int min_thread_count,
            unsigned int max_thread_count)
{
    return pImpl->set_thread_count_range(min_thread_count, max_thread_count);
}

}} // namespace nebula::utility

